package com.lhczf.lucenedb.threads;

import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.document.*;
import org.apache.lucene.facet.*;
import org.apache.lucene.facet.taxonomy.FastTaxonomyFacetCounts;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.index.*;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.MMapDirectory;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

import static org.apache.lucene.search.BooleanClause.Occur.MUST;

@Slf4j
public class MergeReportThread extends Thread {

    public MergeReportThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        log.info("进入报表预统计线程");
        LocalDateTime start = LocalDateTime.now();
//        statisticsReport(directoryList);
        try {
            statisticsReport(0L, Long.MAX_VALUE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        LocalDateTime end = LocalDateTime.now();
        Duration duration = Duration.between(start, end);
        log.info("索引的合并前的报表预处理操作完成，耗时：{}分钟。", duration.toMinutes());
    }

    // 索引路径 历史数据
    public static final String HISTORY_FILE_PATH = "/data/lucene/index/other/index";
    //    public String historyReportFilePath = "/data/lucene/index/other/report";
    // 今天或者前几天的目录
    public static final String FILE_PATH = "/data/lucene/index/days/";
    // 统计路径
    public static final String TAXO_PATH = "/data/lucene/index/other/taxonomy";

    private static final String happenTime = "happenTime";
    private static final String protectObjectName = "protectObjectName";
    private static final String visitTool = "visitTool";
    private static final String srcIp = "srcIp";
    private static final String dbUser = "dbUser";
    private static final String operNum = "operNum";
    private static final String operType = "operType";

    public Boolean statisticsReport(Long startHappenTime, Long endHappenTime) throws IOException {
        LocalDateTime start = LocalDateTime.now();
        IndexSearcher searcher = getIndexSearcher();
        FacetsCollector fc = new FacetsCollector();
        BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
        Query HappenTimeQuery = LongPoint.newRangeQuery(happenTime, startHappenTime, endHappenTime);
        booleanQuery.add(HappenTimeQuery, MUST);

        FacetsCollector.search(searcher, booleanQuery.build(), 10, fc);

        Directory taxoDir = MMapDirectory.open(Paths.get(TAXO_PATH));
        DirectoryTaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);

        FacetsConfig facetsConfig = new FacetsConfig();

       /* Facets facetsAll = new FastTaxonomyFacetCounts(taxoReader, facetsConfig, fc);
        List<FacetResult> resultsAll = facetsAll.getAllDims(10);

        Set<String> protectObjSet = new LinkedHashSet<>();
        Set<String> visitToolSet = new LinkedHashSet<>();
        Set<String> srcIpSet = new LinkedHashSet<>();
        Set<String> dbUserSet = new LinkedHashSet<>();
        Set<String> operTypeSet = new LinkedHashSet<>();
        for (FacetResult tmp : resultsAll) {
            String fieldName = tmp.dim;
            switch (fieldName) {
                case protectObjectName:
                    Arrays.stream(tmp.labelValues).forEach(lab -> protectObjSet.add(lab.label));
                    break;
                case visitTool:
                    Arrays.stream(tmp.labelValues).forEach(lab -> visitToolSet.add(lab.label));
                    break;
                case srcIp:
                    Arrays.stream(tmp.labelValues).forEach(lab -> srcIpSet.add(lab.label));
                    break;
                case operType:
                    Arrays.stream(tmp.labelValues).forEach(lab -> operTypeSet.add(lab.label));
                    break;
                case dbUser:
                    Arrays.stream(tmp.labelValues).forEach(lab -> dbUserSet.add(lab.label));
                    break;
            }
        }*/
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//        start = LocalDateTime.now();
        DrillDownQuery drillDownQuery = new DrillDownQuery(facetsConfig, booleanQuery.build());
        FacetsCollector fc1 = new FacetsCollector(); //要new新collector，否则会累加
        FacetsCollector.search(searcher, drillDownQuery, Integer.MAX_VALUE, fc1);

        LocalDateTime end = LocalDateTime.now();
        Duration duration = Duration.between(start, end);
        log.debug("第一次search耗时" + duration.toMillis());
        start = LocalDateTime.now();


        Facets facets = new FastTaxonomyFacetCounts(taxoReader, facetsConfig, fc1);
        FacetResult results = facets.getTopChildren(Integer.MAX_VALUE, protectObjectName);
        if (results == null) {
            log.debug("没有数据产生");
            return true;
        }
        int total = 0;
        int totalInsert = 0;
//        log.debug("总共产生记录：" + results.labelValues[0].value);
        for (LabelAndValue protectObjectNameLabel : results.labelValues) {
            drillDownQuery = new DrillDownQuery(facetsConfig, booleanQuery.build());
            drillDownQuery.add(protectObjectName, protectObjectNameLabel.label);
            FacetsCollector protectObjectNameFc = new FacetsCollector();//要new新collector，否则会累加
            FacetsCollector.search(searcher, drillDownQuery, Integer.MAX_VALUE, protectObjectNameFc);
            Facets protectObjectNameFacets = new FastTaxonomyFacetCounts(taxoReader, facetsConfig, protectObjectNameFc);
            FacetResult protectObjectNameResults = protectObjectNameFacets.getTopChildren(Integer.MAX_VALUE, visitTool);
            for (LabelAndValue visitToolLabel : protectObjectNameResults.labelValues) {
                DrillDownQuery drillDownQueryVisit = new DrillDownQuery(facetsConfig, booleanQuery.build());
                drillDownQueryVisit.add(protectObjectName, protectObjectNameLabel.label);
                drillDownQueryVisit.add(visitTool, visitToolLabel.label);
                FacetsCollector visitToolFc = new FacetsCollector();//要new新collector，否则会累加
                FacetsCollector.search(searcher, drillDownQueryVisit, Integer.MAX_VALUE, visitToolFc);
                Facets srcIpFacets = new FastTaxonomyFacetCounts(taxoReader, facetsConfig, visitToolFc);
                FacetResult srcIpResults = srcIpFacets.getTopChildren(Integer.MAX_VALUE, srcIp);
                for (LabelAndValue srcIpLabel : srcIpResults.labelValues) {
                    DrillDownQuery drillDownQuerySrcIp = new DrillDownQuery(facetsConfig, booleanQuery.build());
                    drillDownQuerySrcIp.add(protectObjectName, protectObjectNameLabel.label);
                    drillDownQuerySrcIp.add(visitTool, visitToolLabel.label);
                    drillDownQuerySrcIp.add(srcIp, srcIpLabel.label);
                    FacetsCollector srcIpFc = new FacetsCollector();//要new新collector，否则会累加
                    FacetsCollector.search(searcher, drillDownQuerySrcIp, Integer.MAX_VALUE, srcIpFc);
                    Facets dbUserFacets = new FastTaxonomyFacetCounts(taxoReader, facetsConfig, srcIpFc);
                    FacetResult dbUserResults = dbUserFacets.getTopChildren(Integer.MAX_VALUE, dbUser);
                    for (LabelAndValue operTypeLabel : dbUserResults.labelValues) {
                        DrillDownQuery drillDownQueryOperType = new DrillDownQuery(facetsConfig, booleanQuery.build());
                        drillDownQueryOperType.add(protectObjectName, protectObjectNameLabel.label);
                        drillDownQueryOperType.add(visitTool, visitToolLabel.label);
                        drillDownQueryOperType.add(srcIp, srcIpLabel.label);
                        drillDownQueryOperType.add(operType, operTypeLabel.label);
                        FacetsCollector operTypeFc = new FacetsCollector();//要new新collector，否则会累加
                        FacetsCollector.search(searcher, drillDownQuerySrcIp, Integer.MAX_VALUE, operTypeFc);
                        Facets operTypeFacets = new FastTaxonomyFacetCounts(taxoReader, facetsConfig, operTypeFc);
                        FacetResult operTypeResults = operTypeFacets.getTopChildren(Integer.MAX_VALUE, dbUser);
                        for (LabelAndValue label : operTypeResults.labelValues) {
                            System.out.println("id\t 时间 \t" + protectObjectNameLabel.label + "\t" + visitToolLabel.label + "\t" +
                                    srcIpLabel.label + "\t" + operTypeLabel.label + "\t" + label.label + "\t" + label.value);
                            // TODO  更新mysql
                            total += label.value.intValue();
                            totalInsert += 1;
                            // 查询是否存在
                            // 存在更新（累加计数），不存在就插入
                        }
                    }
                }
            }
        }
        end = LocalDateTime.now();
        System.out.println("处理记录：" + total + "条,插入到库中："+totalInsert+"条，执行多次时间(ms):" + (Duration.between(start, end).toMillis()));

        // 执行完更新统计点
        return true;
    }


    private LabelAndValue getResult(FacetsConfig facetsConfig, LabelAndValue labelAndValue, String[] fields, int point) {
//        DrillDownQuery drillDownQuery = new DrillDownQuery(facetsConfig, booleanQuery.build());
//        drillDownQuery.add(protectObjectName, labelAndValue.label);
        return null;
    }

    public IndexSearcher getIndexSearcher() {
        MultiReader multiReader = getMultiReader();
        int totalAll = multiReader.maxDoc();//所有文档数
        log.debug("audit_record中所有文档数:{}", totalAll);
        return new IndexSearcher(multiReader);
    }


    public MultiReader getMultiReader() {
        List<IndexReader> readers = new ArrayList<>();
        IndexReader historyReader = getReaderByPath(HISTORY_FILE_PATH);
        if (historyReader != null) {
            readers.add(historyReader);
        }
        MultiReader multiReader = getTodayMultiReader(readers);
        if (multiReader == null) {
            return null;
        }
        return multiReader;
    }

    /**
     * 得到对应文件夹reader对象
     *
     * @param filePath filePath
     * @return reader IndexReader
     */
    private IndexReader getReaderByPath(String filePath) {
        IndexReader reader = null;
        try {
            Directory dir = FSDirectory.open(Paths.get(filePath));
            reader = DirectoryReader.open(dir);
            log.info("{}文件夹有数据", filePath);
        } catch (IOException e) {
            log.error("{}文件夹为空或异常", filePath, e.toString().substring(100));
        }
        return reader;
    }


    /**
     * 获取今天的reader
     *
     * @param readers 不为空
     * @return
     */
    public MultiReader getTodayMultiReader(List<IndexReader> readers) {
        // 历史索引
        List<String> indexList = new ArrayList<>(16);
        MultiReader multiReader;// today 下的所有索引
        File todayFile = new File(FILE_PATH);
        File[] files = todayFile.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    String[] list = file.list();
                    if (list != null) {
                        Arrays.stream(list).forEach(e -> indexList.add(file + File.separator + e));
                    }
                }
            }
        }
        indexList.forEach(index -> {
            IndexReader readerByPath = getReaderByPath(index);
            if (readerByPath != null) {
                readers.add(readerByPath);
            }
        });

        int totalIndex = readers.size();
        IndexReader[] indexReaders = new IndexReader[totalIndex];
        for (int i = 0; i < totalIndex; i++) {
            indexReaders[i] = readers.get(i);
        }

        try {
            multiReader = new MultiReader(indexReaders);
        } catch (IOException e) {
            log.error("获取多个文件夹一起查询reader有误", e);
            return null;
        }
        return multiReader;
    }


    /**
     * 需要在合并前把数据统计到报表的索引中
     *
     * @param directoryList 需要统计的路径
     */
    private void statisticsReport(List<Directory> directoryList) {
        MultiReader multiReader = getMultiReader(directoryList);
        if (multiReader == null) {
            return;
        }
//        IndexSearcher searcher = new IndexSearcher(multiReader);
        mergeClientAccesses(multiReader);

    }

    private MultiReader getMultiReader(List<Directory> directoryList) {
        Directory[] directories = new Directory[directoryList.size()];
        int index = 0;
        List<IndexReader> list = new ArrayList<>();
        for (Directory directory : directoryList) {
            directories[index] = directory;
            try {
                IndexReader indexReader = DirectoryReader.open(directories[index]);
                if (indexReader != null) {
                    list.add(indexReader);
                }
            } catch (IOException e) {
                log.error("添加异常{}", e);
            }
            index++;
        }
        IndexReader[] indexReaders = new IndexReader[list.size()];
        for (int i = 0; i < indexReaders.length; i++) {
            indexReaders[i] = list.get(i);
        }
        MultiReader multiReader = null;
        try {
            multiReader = new MultiReader(indexReaders);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return multiReader;
    }

    public Set<String> distinctByFieldName(String fieldName, MultiReader multiReader) {
        Set<String> distinctDataSet = new LinkedHashSet<>();

        if (multiReader.leaves().isEmpty()) {
            return new LinkedHashSet<>();
        }
        List<LeafReaderContext> leaves = multiReader.leaves();
        try {
            for (LeafReaderContext leafReaderContext : leaves) {
                SortedDocValues docVals = null;
                int ord;
                while ((ord = docVals.nextDoc()) != SortedSetDocValues.NO_MORE_ORDS) {
                    if (ord >= docVals.getValueCount()) {
                        break;
                    }

                    docVals = DocValues.getSorted(leafReaderContext.reader(), fieldName);

                    String name = docVals.lookupOrd(ord).utf8ToString();
                    distinctDataSet.add(name);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return distinctDataSet;
    }


    /**
     * 获取满足query的所有对象组成list
     *
     * @param multiReader 查询条件
     * @return 所有对象列表
     */
    private Boolean mergeClientAccesses(MultiReader multiReader) {
        BooleanQuery.Builder boolQuery = new BooleanQuery.Builder();
        IndexSearcher searcher = new IndexSearcher(multiReader);

        Set<String> protectObjectNameSet = distinctByFieldName(protectObjectName, multiReader);
        Set<String> visitToolSet = distinctByFieldName(visitTool, multiReader);
        Set<String> srcIpSet = distinctByFieldName(srcIp, multiReader);
        Set<String> dbUserSet = distinctByFieldName(dbUser, multiReader);
        List<Document> list = new ArrayList<>(1024);
        Long today = new Date().getTime() / 1000;
        for (String protectObjectNameStr : protectObjectNameSet) {
            BooleanQuery.Builder boolQueryPro = new BooleanQuery.Builder();
            boolQueryPro.add(boolQuery.build(), MUST);
            boolQueryPro.add(new TermQuery(new Term(protectObjectName, protectObjectNameStr)), MUST);
            for (String srcIpStr : srcIpSet) {
                BooleanQuery.Builder boolQuerySrcIp = new BooleanQuery.Builder();
                boolQuerySrcIp.add(boolQueryPro.build(), MUST);
                boolQuerySrcIp.add(new TermQuery(new Term(srcIp, srcIpStr)), MUST);
                for (String dbUserStr : dbUserSet) {
                    BooleanQuery.Builder boolQuerydbUser = new BooleanQuery.Builder();
                    boolQuerydbUser.add(boolQuerySrcIp.build(), MUST);
                    boolQuerydbUser.add(new TermQuery(new Term(dbUser, dbUserStr)), MUST);
                    for (String visitToolStr : visitToolSet) {
                        BooleanQuery.Builder boolQueryVisitTool = new BooleanQuery.Builder();
                        boolQueryVisitTool.add(boolQuerydbUser.build(), MUST);
                        boolQueryVisitTool.add(new TermQuery(new Term(visitTool, visitToolStr)), MUST);
                        try {
                            int search = searcher.count(boolQuerySrcIp.build());
                            if (search != 0) {
                                System.out.println(visitToolStr + "\t" + protectObjectNameStr + "\t" + srcIpStr + "\t" + dbUserStr + "\t total=" + search);
                                Document doc = new Document();
                                doc.add(new StringField(protectObjectName, protectObjectNameStr, Field.Store.YES));
                                doc.add(new StringField(srcIp, srcIpStr, Field.Store.YES));
                                doc.add(new StringField(dbUser, dbUserStr, Field.Store.YES));
                                doc.add(new StringField(visitTool, visitToolStr, Field.Store.YES));

                                doc.add(new LongPoint(happenTime, today));
                                doc.add(new StoredField(happenTime, today));

                                doc.add(new LongPoint(operNum, search));
                                doc.add(new StoredField(operNum, search));
                                list.add(doc);
                            }
                        } catch (IOException e) {
                            log.error("{}", e);
                        }
                    }
                }
            }
        }
        // TODO 拿到对应报表的indexWriter
        // 插入list
        return true;
    }

}
